//
//  EzSocketServer.cpp
//  FFTEST_RTSP_TCP
//
//  Created by 刘跃龙 on 1/3/18.
//  Copyright © 2018 absolutemobile. All rights reserved.
//

#include "EzMulticastServer.h"

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <vector>
#include <iostream>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include "AccountData.h"

#include "EzText.h"

EZ_BEGIN

EzMulticastServer::EzMulticastServer(std::string multicastIP,unsigned short int multicastPort)
:m_multicastIP(multicastIP)
,m_multicastPort(multicastPort)
,m_socket(-1)
{
}
EzMulticastServer::~EzMulticastServer()
{
}

void* thread_Revc(void* pdata){
    EzMulticastServer* pEzMulticastServer = (EzMulticastServer*)pdata;
    while(pEzMulticastServer->m_isWork){
        sleep(1);
        if( time(NULL) < 1514764800 || AccountData::getInstance()->getGetServerInfoStatus() != 12){
            if(pEzMulticastServer->m_isWork) continue; else break;
        }
        
        int err = -1;
        struct sockaddr_in serverAddr;
        char buf[EzMulticastServer_Socket_Buffer_Size];
        
        pEzMulticastServer->m_socket = socket(AF_INET, SOCK_DGRAM, 0);
        if(pEzMulticastServer->m_socket < 0){
            pEzMulticastServer->m_socket = -1;
            if(pEzMulticastServer->m_isWork) continue; else break;
        }
        
        
        
        /*---- Configure settings of the server address struct ----*/
        memset(&serverAddr, 0, sizeof(serverAddr));
        /* Address family = Internet */
        serverAddr.sin_family = AF_INET;
        /* Set port number, using htons function to use proper byte order */
        serverAddr.sin_port = htons(pEzMulticastServer->m_multicastPort);
        /* Set IP address to localhost */
        serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        /* Set all bits of the padding field to 0 */
        memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
        
//        struct timeval timeout2 = {300,0};
//        err = setsockopt(pEzMulticastServer->m_socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout2,sizeof(struct timeval));
//        if(err < 0)
//        {
//            perror("setsockopt():SO_RCVTIMEO");
//            close(pEzMulticastServer->m_socket);
//            pEzMulticastServer->m_socket = -1;
//            if(pEzMulticastServer->m_isWork) continue; else break;
//        }
        
        int loop = 0;
        err = setsockopt(pEzMulticastServer->m_socket,IPPROTO_IP, IP_MULTICAST_LOOP,&loop, sizeof(loop));
        if(err < 0)
        {
            perror("setsockopt():IP_MULTICAST_LOOP");
            close(pEzMulticastServer->m_socket);
            pEzMulticastServer->m_socket = -1;
            if(pEzMulticastServer->m_isWork) continue; else break;
        }
        
        int opt = 1;
        err = setsockopt(pEzMulticastServer->m_socket,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
        if(err < 0)
        {
            perror("setsockopt():SO_REUSEADDR");
            close(pEzMulticastServer->m_socket);
            pEzMulticastServer->m_socket = -1;
            if(pEzMulticastServer->m_isWork) continue; else break;
        }
        
        /*---- Bind the address struct to the socket ----*/
        if(bind(pEzMulticastServer->m_socket, (struct sockaddr *) &serverAddr, sizeof(serverAddr))==0)
        {
            std::cout << "bind [" << inet_ntoa(serverAddr.sin_addr) << ":" << ntohs(serverAddr.sin_port) << "]" << std::endl;
        }else{
            perror("bind error");
            close(pEzMulticastServer->m_socket);
            pEzMulticastServer->m_socket = -1;
            if(pEzMulticastServer->m_isWork) continue; else break;
        }
        
        struct ip_mreq mreq;
        mreq.imr_multiaddr.s_addr = inet_addr(pEzMulticastServer->m_multicastIP.c_str()); /*多播地址*/
        mreq.imr_interface.s_addr = htonl(INADDR_ANY); /*网络接口为默认*/
        
        err = setsockopt(pEzMulticastServer->m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq));/*将本机加入多播组*/
        if(err < 0)
        {
            perror("setsockopt():IP_ADD_MEMBERSHIP");
            close(pEzMulticastServer->m_socket);
            pEzMulticastServer->m_socket = -1;
            if(pEzMulticastServer->m_isWork) continue; else break;
        }
        
        while(pEzMulticastServer->m_isWork){
            struct sockaddr_in client_addr;
            int client_len = sizeof(client_addr);
            memset(buf, 0, EzMulticastServer_Socket_Buffer_Size);                 /*清空接收缓冲区*/
            ssize_t recv_size = recvfrom(pEzMulticastServer->m_socket,buf,EzMulticastServer_Socket_Buffer_Size, 0,(struct sockaddr*)&client_addr,(socklen_t*)&client_len);
            if( recv_size > 0 ){
                //[1002e1337f0000010000000000000000]
                //10 ==> 16 byte
                //02 ==> AF_INET
                //e133 ==> ntohs(e133) ==> port(13281)
                //7f000001 ==> 127.0.0.1
                
                std::string ip = inet_ntoa(client_addr.sin_addr);
                unsigned short int port = ntohs(client_addr.sin_port);
                
                //                std::cout << "client data [" << Ez::EzText::StringToHexString(std::string((char*)(&client_addr),(size_t)client_len)) << "]" << std::endl;
                //                std::cout << "client IP [" << ip << "]" << std::endl;
                //                std::cout << "client PORT [" << port << "]" << std::endl;
                
                std::string request = std::string(buf,recv_size);
                if(pEzMulticastServer->m_callback) pEzMulticastServer->m_callback(ip,port,request);
            }else{
                if(errno==EINTR){
                    continue;
                }else if( errno==EAGAIN || errno==EWOULDBLOCK ){
                    perror("recv timeout:");
                    break;
                }else{
                    perror("recv error:");
                    break;
                }
            }
        }
        err = setsockopt(pEzMulticastServer->m_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP,&mreq, sizeof(mreq));/*将本机加入多播组*/
        if(err < 0) perror("setsockopt():IP_DROP_MEMBERSHIP");
        close(pEzMulticastServer->m_socket);
        pEzMulticastServer->m_socket = -1;
        if(pEzMulticastServer->m_isWork) continue; else break;
    }
    pthread_exit(NULL);
    return NULL;
}

bool EzMulticastServer::start()
{
    m_isWork = true;
    pthread_create(&m_thread, NULL, &thread_Revc, this);
    return true;
}

bool EzMulticastServer::stop()
{
    if(m_socket!=-1){
        close(m_socket);
    }
    if(m_isWork)
    {
        m_isWork = false;
        pthread_join(m_thread, NULL);
    }
    return true;
}

bool EzMulticastServer::multicastData(std::string data)
{
    if( m_socket > 0 && m_isWork )
    {
        //        printf("send:%s\n",data.c_str());
        struct sockaddr_in mcast_addr;
        memset(&mcast_addr, 0, sizeof(mcast_addr));
        mcast_addr.sin_family = AF_INET;
        mcast_addr.sin_addr.s_addr = inet_addr(m_multicastIP.c_str());
        mcast_addr.sin_port = htons(m_multicastPort);
        sendto(m_socket,data.c_str(),data.size(),0,(struct sockaddr*)&mcast_addr,sizeof(mcast_addr));//FIXME 返回值处理
        return true;
    }
    return false;
}

bool EzMulticastServer::unicastData(std::string ip,unsigned short int port,std::string data)
{
    if( m_socket > 0 && m_isWork )
    {
//        printf("send:%s\n",data.c_str());
        struct sockaddr_in mcast_addr;
        memset(&mcast_addr, 0, sizeof(mcast_addr));
        mcast_addr.sin_family = AF_INET;
        mcast_addr.sin_addr.s_addr = inet_addr(ip.c_str());
        mcast_addr.sin_port = htons(port);
        sendto(m_socket,data.c_str(),data.size(),0,(struct sockaddr*)&mcast_addr,sizeof(mcast_addr));//FIXME 返回值处理
        return true;
    }
    return false;
}

EZ_END

